package common.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import common.bean.RRException;
import common.sysconfig.MinioConfig;
import common.vo.Scenario;
import common.vo.ViewContentType;

import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.io.IOUtils;

import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;

import io.minio.messages.Item;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import static org.opencv.imgcodecs.Imgcodecs.imencode;


@Component
@Slf4j

public class MinioUtil {
    @Autowired
    private MinioConfig prop;
    @Autowired
    private MinioClient minioClient;

    /**
     * im转Mat
     *
     * @param im
     * @return
     * @throws IOException
     */
    public static Mat convertMat(InputStream im) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(im);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        while ((bytesRead = bis.read(buffer)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.flush();
        os.close();
        bis.close();

        Mat encoded = new Mat(1, os.size(), 0);
        encoded.put(0, 0, os.toByteArray());

        Mat decoded = Imgcodecs.imdecode(encoded, -1);
        return decoded;
    }

    public static InputStream coverInput(Mat mat) {
        MatOfByte mob = new MatOfByte();
        imencode(".jpg", mat, mob);
        byte[] byteArray = mob.toArray();
        return new ByteArrayInputStream(byteArray);
    }

    @SneakyThrows
    public static void main(String[] args) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinioClient minioClient =
                MinioClient.builder()
                        .endpoint("http://10.1.1.13:9000")
                        .credentials("i2twmx", "qf3.1415")
                        .build();

        BufferedInputStream inputStream = FileUtil.getInputStream("D:\\Downloads\\Pleiotropic effects of probenecid on three-dimensional .pdf");
        ObjectWriteResponse oss = minioClient.putObject(
                PutObjectArgs.builder().bucket("oss").object("Pleiotropic effects of probenecid on three-dimensional .pdf").stream(
                                inputStream, -1, 10485760)
                        .contentType(ViewContentType.getContentType("Pleiotropic effects of probenecid on three-dimensional .pdf"))
                        .build());


        System.out.println(111);

    }

    /**
     * 查看存储bucket是否存在
     *
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     *
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 创建存储bucket
     *
     * @return Boolean
     */
    public String getUrl(String path) {
        try {
//            String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(prop.getBucketName()).object(path).method(Method.GET).expiry(1, TimeUnit.HOURS).build());
            return "/" + prop.getBucketName() + "/" + path;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 删除存储bucket
     *
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     */
    public List<Bucket> getAllBuckets() {
        try {
            List<io.minio.messages.Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param im       文件
     * @param uid      用户ID
     * @param date     时间
     * @param fileName 文件名
     * @param scenario 场景
     * @return
     */
    public String upload(InputStream im, Integer uid, String date, String fileName, Scenario scenario) {
        if (null == im) {
            throw new RRException("上传文件为空");
        }
        String template = "{}/{}/{}/" + RandomUtil.randomString(5) + "_{}";

        try {
            PutObjectArgs objectArgs =
                    PutObjectArgs
                            .builder()
                            .contentType(ViewContentType.getContentType(fileName))
                            .bucket(prop.getBucketName())
                            .object(StrUtil.format(template, scenario, uid, date, fileName))
                            .stream(im, -1, 10485760).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return StrUtil.format(template, scenario, uid, date, fileName);
    }

    /**
     * @param im       文件
     * @param fileName 文件名
     * @return
     */
    public String upload(InputStream im, String fileName, String uid) {
        String date = DateUtil.today();
        if (null == im) {
            throw new RRException("上传文件为空");
        }
        String template = "/{}/{}/{}/" + RandomUtil.randomString(5) + "_{}";

        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().contentType(ViewContentType.getContentType(fileName)).bucket(prop.getBucketName()).object(StrUtil.format(template, "interior", uid, date, fileName)).stream(im, im.available(), -1).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return StrUtil.format(template, "interior", uid, date, fileName);
    }

    /**
     * 预览图片
     *
     * @param fileName
     * @return
     */
    public String preview(String fileName) {
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     *
     * @param fileName 文件名称
     * @param res      response
     * @return Boolean
     */
    public void download(String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = response.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                // 设置强制下载不打开
                // res.setContentType("application/force-download");
                res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                try (ServletOutputStream stream = res.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件下载Im
     *
     * @param fileName 文件名称
     * @return Boolean
     */
    public GetObjectResponse downloadIm(String fileName) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();

        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            return response;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载Im
     *
     * @param fileName 文件名称
     * @return Boolean
     */
    public byte[] downloadByte(String fileName) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();

        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            return IOUtils.toByteArray(response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Mat downloadMat(String fileName) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();

        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            return convertMat(response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public BufferedImage fetchImage(String fileName) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();

        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            return ImageIO.read(response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查看文件对象
     *
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects() {
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(prop.getBucketName()).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 配置读写权限
     *
     * @param bucketName
     * @return
     * @throws Exception
     */
    public boolean makeBucketPolicy(String bucketName) {
        String config = "{\"Version\":\"2012-10-17\"," + "\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":" + "{\"AWS\":[\"*\"]},\"Action\":[\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"," + "\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";

        SetBucketPolicyArgs build = SetBucketPolicyArgs.builder().bucket(bucketName).config(config).build();

        boolean flag = bucketExists(bucketName);
        if (!flag) {
            try {
                makeBucket(bucketName);
                minioClient.setBucketPolicy(build);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }
        return true;
    }

    /**
     * base64转inputStream流
     *
     * @param base64str
     * @return
     */
    public InputStream baseToInputStream(String base64str) {
        ByteArrayInputStream stream = null;
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] bytes = decoder.decodeBuffer(base64str);
            stream = new ByteArrayInputStream(bytes);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return stream;
    }


}
